﻿#region  
//=====================================================
// 文件名: IResourceLoad                
// 创建者: #AuthorName#                
// 创建时间: #CreateTime#              	
// Email: #AuthorEmail#               
// 描述: 当前脚本的功能              
// 修改者列表: 修改者名字以及修改功能      
// 版权所有: #Copyright#                 
//======================================================
#endregion 



using FrameWork.AB;
using FrameWork.ObjectPool;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace FrameWork.Reource
{
    //资源加载优先级
    public enum LoadResPriority
    {
        RES_HIGHT = 0,
        RES_MIDDLE,
        RES_SLOW,
        RES_NUM,
    }

    //资源加载承载类
    public class AsyncLoadResParm
    {
        public List<AsyncCallBack> mCallBackList = new List<AsyncCallBack>();
        public uint mCrc;
        public string mPath;
        public System.Type mType;
        public LoadResPriority mPriority = LoadResPriority.RES_SLOW;//加载优先级，默认最小
        public void Reset()
        {
            mCallBackList.Clear();
            mCrc = 0;
            mPath = string.Empty;
            mPriority = LoadResPriority.RES_SLOW;
        }
    }
    public class AsyncCallBack
    {
        public OnAsyncObjFinish mDealFinish = null;
        public object[] mParme;

        public void Reset()
        {
            mDealFinish = null;
            mParme = null;
        }
    }

    public abstract class IResourceLoad
    {

        private readonly Dictionary<uint, ResourceItem> m_ResourceItemDic = new Dictionary<uint, ResourceItem>();

        public CMapList<ResourceItem> m_noRefrenceAssetList = new CMapList<ResourceItem>();                                 //缓存引用计数为0的资源列表，达到缓存最大时释放这个链表内很久没用的资源

        protected MonoBehaviour mMono;

        protected List<AsyncLoadResParm>[] mLoadingAssetList = new List<AsyncLoadResParm>[(int)LoadResPriority.RES_NUM];    //异步加载list

        protected Dictionary<uint, AsyncLoadResParm> mLoadingAssetDic = new Dictionary<uint, AsyncLoadResParm>();           //正在加载的资源

        protected CalssObjectPool<AsyncLoadResParm> mAsyncLoadResParamPool = new CalssObjectPool<AsyncLoadResParm>(50);     //异步加载类的对象池

        protected CalssObjectPool<AsyncCallBack> mAsyncCallBackPool = new CalssObjectPool<AsyncCallBack>(100);              //异步加载回调对象池

        protected const long MAXLOADRESTIME = 2000000;                                                                      //最长连续卡着加载资源的时间，单位（微秒）

        // 初始化异步资源加载
        public virtual void Init(MonoBehaviour mono)
        {
            for (int i = 0; i < (int)LoadResPriority.RES_NUM; i++)
            {
                mLoadingAssetList[i] = new List<AsyncLoadResParm>();
            }
            
            mMono = mono;
        }

        // 同步资源加载，外部调用，仅加载不需要实例化的资源，例如  Texture 音频的等等。
        public abstract T LoadResource<T>(string path) where T : Object;

        // 异步资源加载
        public void AsyncLoadResource<T>(string path, OnAsyncObjFinish dealfinish, LoadResPriority priority, uint crc = 0, params object[] param)
        {
            if (crc == 0)
            {
                crc = CRC32.GetCRC32(path);
            }

            ResourceItem item = GetCacheResourceItem(crc);

            if (item != null)
            {
                dealfinish?.Invoke(path, item.m_obj, priority, param);
                return;
            }

            //不是正在加载中，添加到加载列表
            if (!mLoadingAssetDic.TryGetValue(crc, out AsyncLoadResParm para) || para == null)
            {
                para = mAsyncLoadResParamPool.Spawn(true);
                para.mCrc = crc;
                para.mPath = path;
                para.mPriority = priority;
                para.mType = typeof(T);
                mLoadingAssetDic.Add(crc, para);
                mLoadingAssetList[(int)priority].Add(para);
            }

            //向回调列表内加回调
            AsyncCallBack callBack = mAsyncCallBackPool.Spawn(true);
            callBack.mDealFinish = dealfinish;
            callBack.mParme = param;
            para.mCallBackList.Add(callBack);

        }

        // 获取resourceitem（资源）
        public ResourceItem GetCacheResourceItem(uint _crc, int _addrefcount = 1)
        {
            if (m_ResourceItemDic.TryGetValue(_crc, out ResourceItem _item))
            {
                _item.RefCount += _addrefcount;
                _item.m_LastUserTime = Time.realtimeSinceStartup;
            }
            return _item;
        }

        // 缓存ResourceItem（资源）
        public void CacheResource(string _path, ref ResourceItem _item, uint _crc, Object _obj, int _addrefcount = 1)
        {
            WashOut();
            if (_item == null)
            {
                Debug.LogError("ResourceItem is null,path:" + _path);
            }
            if (_obj == null)
            {
                Debug.LogError("ResourceLoad fail:" + _path);
            }
            _item.m_obj = _obj;
            _item.m_Guid = _obj.GetInstanceID();
            _item.m_LastUserTime = Time.realtimeSinceStartup;
            _item.RefCount += _addrefcount;
            if (m_ResourceItemDic.ContainsKey(_item.Crc))
            {
                m_ResourceItemDic[_item.Crc] = _item;
            }
            else
            {
                m_ResourceItemDic.Add(_item.Crc, _item);
            }
        }

        // 内存到达极限使清除最早未使用的资源
        public void WashOut()
        {
            ////当前内存使用大于80%  就清除最早使用的资源
            //{
            //    if (m_noRefrenceAssetList.NodeCount <= 0)
            //    {
            //        break;
            //    }

            //    ResourceItem item = m_noRefrenceAssetList.GetTailNode();
            //    DestoryResourceItem(item, true);
            //    m_noRefrenceAssetList.PopInFromTheTail();
            //}

        }

        //释放资源
        public bool ReleaseResource(Object obj, bool destoryObj = false)
        {
            if (obj == null)
            {
                return false;
            }

            ResourceItem item = null;
            foreach (var res in m_ResourceItemDic.Values)
            {
                if (res.m_Guid == obj.GetInstanceID())
                {
                    item = res;
                }
            }

            return ReleaseResource(item, destoryObj);

        }

        //释放资源
        public bool ReleaseResource(string path, bool destoryObj = false)
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new System.ArgumentException($"“{nameof(path)}”不能是 Null 或为空", nameof(path));
            }
            uint crc = CRC32.GetCRC32(path);
            if (!m_ResourceItemDic.TryGetValue(crc, out ResourceItem item ) || null == item)
            {
                Debug.LogError("m_ResourceItemDic内不存在这个资源：" + path);
            }

            return ReleaseResource(item, destoryObj);

        }

        //释放资源
        public bool ReleaseResource(ResourceItem item, bool destoryObj = false)
        {
            if (item == null)
            {
                Debug.LogErrorFormat("Unfound:{0},May have been reslease.", item);
                return false;
            }

            item.RefCount--;

            DestoryResourceItem(item, destoryObj);

            return true;
        }

        // 删除资源
        public abstract void DestoryResourceItem(ResourceItem item, bool destoryCache = false);

        // 判断是否达到删除此资源的条件
        protected bool CanDestoryResourceItem(ResourceItem item, bool destoryCache = false)
        {
            if (item == null || item.RefCount > 0)
            {
                return false;
            }

            if (!destoryCache)
            {
                //m_noRefrenceAssetList.InsertToHead(item);
                return false;
            }

            if (!m_ResourceItemDic.Remove(item.Crc))
            {
                return false;
            }

        
            return true;
        }

        //清空不需要的缓存（一般在切换场景时）
        public void Clear()
        {
            List<ResourceItem> TempItems = new List<ResourceItem>();
            foreach (ResourceItem item in m_ResourceItemDic.Values)
            {
                if (item.m_Clear)
                {
                    TempItems.Add(item);
                }
            }
            foreach (ResourceItem item in TempItems)
            {
                DestoryResourceItem(item,true);

            }

            TempItems.Clear();
        }

        //预加载
        public abstract void PreloadRes(string path);


    }
}